// h函数
function createElement(type, props, children, key) {
    return {
        type, 
        props,
        children,
        key
    }
}

const vDom = createElement(
    'ul',
    {
        class: 'list',
        style: 'width: 300px; height: 300px; background-color: orange'
    },
    [
        // {
        //     type: 'text',
        //     content: 'before patch'
        // },
        createElement(
            'li',
            {
                class: 'item',
                'data-index': 0
            },
            [
                // createElement('p', { class: 'text' }, [{ type: 'text', content: '第一个列表项' }])
            ],
            '001'
        ),
        createElement(
            'li',
            {
                class: 'item',
                'data-index': 1
            },
            [
                createElement(
                    'p', 
                    { class: 'text' }, 
                    [
                        createElement(
                            'span', 
                            {
                                class: "title",
                            },
                            [
                                {
                                    type: 'text',
                                    content: '第2个列表项'
                                }
                            ]
                        )
                    ]
                )
            ],
            '002'
        ),
        createElement(
            'li',
            {
                class: 'item',
                'data-index': 2
            },
            [
                {
                    type: 'text',
                    content: '第3个列表项'
                }
            ],
            '003'
        ),
    ]
)

// console.log(vDom);

// vDom 简化为只有两种情形：文本节点 or 标签对象

// ******建立一个更简易的vDom对象******——————————————————>要么有children，里面都是vDom对象，对应标签元素；要么有text，即当前标签里只有文本结点
function render(vDom) {

    if(vDom.type === 'text') return document.createTextNode(vDom.content);

    const { type, props, children } = vDom;

    const el = vDom.el = document.createElement(type);

    setProps();

    children.map((child) => {
        const cEl = child.el = render(child);
        el.appendChild(cEl);
        return cEl;
    })
    
    el._value = vDom;

    return el;
}

function setProps() {
    // console.log("set props for vNode");
}

const rDom = render(vDom);
console.log(rDom);
const root = document.querySelector('#root');
root.appendChild(rDom);

const vDom2 = createElement(
    'ul',
    {
        class: 'list-wrap',
        style: 'width: 300px; height: 300px; background-color: orange'
    },
    [
        // {
        //     type: 'text',
        //     content: 'after patch'
        // },
        createElement(
            'li',
            {
                class: 'item',
                'data-index': 0
            },
            [
                createElement('p', { class: 'title' }, [{ type: 'text', content: '特殊列表项' }])
            ],
            '003'
        ),
        createElement(
            'li',
            {
                class: 'item',
                'data-index': 1
            },
            [
                createElement(
                    'p', 
                    { class: 'text' }, 
                    [{ type: 'text', content: 'hello world' }]
                )
            ],
            '002'
        ),
        createElement(
            'div',
            {
                class: 'item',
                'data-index': 2
            },
            [
                {
                    type: 'text',
                    content: '第3个列表项'
                }
            ],
            '001'
        ),
    ]
)

// console.log(vDom2);

function patch(oldVDom, newVDom) {
    let oldRDom = oldVDom.el; // 老的真实dom

    // ——————————不同类标签直接替换——————————
    if(oldVDom && !newVDom) { // 新的虚拟结点不存在，直接删除对应的dom
        oldRDom.parentNode.removeChild(oldRDom);
        return;
    }
    if(newVDom.type !== oldVDom.type) {
        const newDom = newVDom.type === 'text'
            ? document.createTextNode(newVDom)
            : render(newVDom);
        oldRDom.parentNode.replaceChild(newDom, oldRDom);
        return;
    }

    // ——————————同类标签进行比较（复用dom，更新其props & children）——————————
    if(newVDom === oldVDom) {  // TODO：相同虚拟dom直接return，这里疑问是新的vDom的来源，数据更新时vDom是新生成的吗？如果vDom复用，这里的逻辑显然有问题
        return;
    } else { // 新旧结点不同（不是同一个对象）
        // 首先真实dom的文本内容对齐新虚拟dom，但当前情景下假设text与标签类型的结点不共存，所以更换文本内容之后直接return即可
        if(newVDom.children[0] && newVDom.children[0].type === 'text') { // 默认文本结点对象就在children数组的第一个
            oldVDom.el.textContent = newVDom.children[0].content;
            return;
        }
        // 对比孩子结点
        const oldChildren = oldVDom.children;
        const newChildren = newVDom.children;
        if(oldChildren.length === 0 && newChildren.length !== 0) { // 新有旧无，直接添加新即可
            for(let c of newChildren) {
                oldRDom.appendChild(render(c));
            }
        } else if(oldChildren.length !== 0 && newChildren.length === 0) { // 旧有新无，直接删除原本的dom
            for(let c of oldChildren) {
                oldRDom.removeChild(c.el);
            }
        } else { // 新旧都有子结点
            // patchChildren(oldChildren, newChildren);
        }
    }
}

function patchChildren(oldChildren, newChildren) {
    let newLeftIdx = 0;
    let newRightIdx = newChildren.length - 1;
    let oldLeftIdx = 0;
    let oldRightIdx = oldChildren.length - 1;
    // 尽可能实现结点复用——结点移动来代替结点创建
    while(newLeftIdx <= newRightIdx && oldLeftIdx <= oldRightIdx) {
        const newLeftNode = newChildren[newLeftIdx];
        const newRightNode = newChildren[newRightIdx];
        const oldLeftNode = oldChildren[oldLeftIdx];
        const oldRightNode = oldChildren[oldRightIdx];

        // TODO: 这里插入没玩明白
        if(isSameVNodeType(newLeftNode, oldLeftNode)) { // 新左跟旧左同类可复用
            patch(oldLeftNode, newLeftNode);
            newLeftIdx ++;
            oldLeftIdx ++;
        } else if(isSameVNodeType(newLeftNode, oldRightNode)) { // 新左跟旧右可复用
            patch(oldRightNode, newLeftNode);
            oldLeftNode.el.parentNode.insertBefore(oldRightNode.el, oldChildren[newLeftIdx].el);
            oldRightIdx --;
            newLeftIdx ++;
        } else if(isSameVNodeType(newRightNode, oldLeftNode)) { // 新右跟旧左可复用
            patch(oldLeftNode, newRightNode);
            oldLeftNode.el.parentNode.insertBefore(oldRightNode.el, oldChildren[newLeftIdx].el);
            newRightIdx --;
            oldLeftIdx ++;
        } else if(isSameVNodeType(newRightNode, oldRightNode)) { // 新右跟旧右可复用
            patch(oldRightNode, newRightNode);
            newRightIdx --;
            oldRightIdx --;
        } else {
            break;
        }
    }
}

// 判断结点是否可以复用（是否为"相同"结点）
function isSameVNodeType(vNode1, vNode2) {
    return vNode1.type === vNode2.type && vNode1.key === vNode2.key;
}
setTimeout(() => {
    patch(rDom._value, vDom2);
}, 1000);
